﻿using System;
using System.Collections.Generic;
using System.Threading;
using SimpleServiceBus.Bus.MessageManagement;
using SimpleServiceBus.Endpoint;
using SimpleServiceBus.Utilities;
using SimpleServiceBus.Utilities.ExpressionEval;

namespace SimpleServiceBus.Bus.Pipeline.PipelineComponents.MessageFiltering
{
    public class MessageFilterService : EndpointServiceBase, IMessageFilterService
    {
        private readonly ReaderWriterLock _lock = new ReaderWriterLock();
        private readonly List<MessageFilterBinding> _receiveFilters = new List<MessageFilterBinding>();
        private readonly List<MessageFilterBinding> _sendFilters = new List<MessageFilterBinding>();
        private IMessageExpressionEvaluator _expressionEvaluator;

        public IMessageExpressionEvaluator ExpressionEvaluator
        {
            get { return _expressionEvaluator; }
        }

        #region IMessageFilterService Members

        public void RegisterMessageFilter(Type messageType, IMessagePredicate predicate, PipelineDirection pipelineDirection)
        {
            using (new WriterLock(_lock, TimeSpan.FromSeconds(10)))
            {
                var binding = new MessageFilterBinding
                                  {
                                      MessageType = messageType,
                                      MessageFilter = predicate
                                  };

                if (pipelineDirection == PipelineDirection.Receive)
                    _receiveFilters.Add(binding);

                else
                    _sendFilters.Add(binding);
            }
        }

        public bool Matches(MessageEnvelope message, PipelineDirection pipelineDirection)
        {
            using (new ReaderLock(_lock, TimeSpan.FromSeconds(10)))
            {
                if (pipelineDirection == PipelineDirection.Receive)
                    return Matches(_receiveFilters, message);
                return Matches(_sendFilters, message);
            }
        }

        #endregion

        private static bool Matches(ICollection<MessageFilterBinding> predicates, MessageEnvelope message)
        {
            if (predicates == null || predicates.Count < 1)
                return true;

            //We'll look for a matching filter that should prevent message processing.
            //If we the filter allows the message, then we'll keep going, in case
            //a denial exists later in the list.
            foreach (MessageFilterBinding predicate in predicates)
                if (predicate.Matches(message) && !predicate.AllowMessage(message))
                    return false;

            return true;
        }

        protected override void StartService()
        {
            _expressionEvaluator = ServiceManager.GetService<IMessageExpressionEvaluator>();
        }

        protected override void ShutDownService()
        {
            _expressionEvaluator = null;
        }
    }
}